package.cpath = "/opt/xyitech/jieyan-backend-production/xbit/?.so"
local xbit = require 'xbit'
package.cpath = "/usr/nginx/?.so;;"
local json = require 'cjson'

if ngx.var.server_port ==  '9885' then
    package.path = "/opt/xyitech/jieyan-backend-sandbox/?.lua;;"
    package.cpath = "/opt/xyitech/jieyan-backend-sandbox/?.so;;"
elseif ngx.var.server_port == '11885' then
    package.path = "/opt/xyitech/jieyan-backend-debug/?.lua;;"
    package.cpath = "/opt/xyitech/jieyan-backend-debug/?.so;;"
else
    package.path = "/opt/xyitech/jieyan-backend-production/?.lua;;"
    package.cpath = "/opt/xyitech/jieyan-backend-production/?.so;;"
end

local require = require
local setmetatable = setmetatable
local error = error
local table = table
local ngx = ngx
local os = os
local string = string
local tonumber = tonumber
local server = require "modules.server"
local redis = require "redis"
local util = require 'util'
local protocal = require 'protocal'

 local _fs = util.getChannel("fs_all")
 local _ws = util.getChannel("ws_all")


local channel_name = "jieyan"

 _int32_t = 0
 _int16_t = 1
 _float = 2
 _uint32_t = 4
 _uint16_t = 5
 _uint8_t = 6
 _int8_t = 7

--create connection
local sock = ngx.req.socket(true)

local fid = nil

local subscribed = false
--create success
if not sock then
  ngx.log(ngx.ERR, "failed to new tcp socket: ", err)
  return ngx.exit(444)
end
 
 
local push = function()
    -- --create redis
    local red = redis:new()
    -- red:set_timeout(5000) -- 1 sec
    local ok, err = red:connect("127.0.0.1", 6379)
    if not ok then
        ngx.log(ngx.ERR, "failed to connect redis: ", err)
        sock:send_close()
        return
    end
 
    --sub
    -- local res, err = red:subscribe(channel_name)
    -- if not res then
    --     ngx.log(ngx.ERR, "failed to sub redis: ", err)
    --     sock:send_close()
    --     return
    -- end

    local res, err = red:subscribe(_fs)
    if not res then
        ngx.log(ngx.ERR, "failed to sub redis: ", err)
        sock:send_close()
        return
    end


    -- loop : read from redis
    while true do
        local res, err = red:read_reply()
        if res then
            local data = res[3]
            -- TODO do data here
            --
            if not sock then
                ngx.log(ngx.ERR, "lost sockect")
                return ngx.exit(444)
            end
            -- sock:settimeout(1000)  -- one second timeout
            if filterData(fid, data) then
                local bytes, err = sock:send(data)
                if not bytes then
                    -- better error handling
                    ngx.log(ngx.ERR, "failed to send text: ", err)
                    return ngx.exit(444)
                end
            end
        else
            if err ~= "timeout" then
                ngx.log(ngx.ERR, "Redis read error: ", err)
                return ngx.exit(444)
            end
        end
    end
end

 function filterData(fid, data)
    local size = _uint32_t
    local msgId = xbit.readBuff(_uint8_t, data, 4)
    local _fid = xbit.readBuff(size, data, 5)
    -- local _fid = xbit.readBuff(size, data, 5)
    -- local len = xbit.readBuff(_uint32_t, data, 0)
    local passMsgIds = {201, 203, 101, 105, 107}
    -- ngx.log(ngx.ERR, len)
    -- ngx.log(ngx.ERR, msgId)
    -- ngx.log(ngx.ERR, fid)
    -- ngx.log(ngx.ERR, _fid)
    -- ngx.log(ngx.ERR, util.contains(passMsgIds, msgId))
    if util.contains(passMsgIds, msgId) then
        if util.contains({201, 203}, msgId) then
            if fid and fid == _fid then
                return data
            end
        else
            return data
        end
    else 
        return nil
    end
end

function response(msgId, fid, buff, red_p)
    local data, args, final = nil, {}, nil
    local _fid = xbit.readBuff(_uint32_t, buff, 1)
    -- ngx.log(ngx.ERR, msgId)
    -- ngx.log(ngx.ERR, fid)
    -- ngx.log(ngx.ERR, _fid)
    if msgId == 155 then
        args = {_uint8_t, 203, _uint32_t, _fid, _uint32_t, os.time()}
        data = xbit.tobit2(unpack(args))
        local prefix = xbit.tobit2(unpack({_uint32_t, #data}))
        final = prefix .. data
    end
    if red_p and final then
        -- ngx.log(ngx.ERR, xbit.tohex(final))
        local byte, err = red_p:publish(_fs, final)
        -- ngx.log(ngx.ERR, byte)
        -- ngx.log(ngx.ERR, err)
    end
end

function gatherData(msgId, fid, body, red_set)
    if msgId == 152 then
        local bodyTable = protocal.toTable(body)
        fid = bodyTable[2]
        red_set:set("pressure_alt_" ..fid, bodyTable[6])
    end
end

local co = ngx.thread.spawn(push)

local red_set = redis:new()
red_set:set_timeout(1000) -- 1 sec
local ok, err = red_set:connect("127.0.0.1", 6379)
if not ok then
    ngx.log(ngx.ERR, "failed to connect redis: ", err)
end

local red_p = redis:new()
red_p:set_timeout(1000) -- 1 sec
local ok, err = red_p:connect("127.0.0.1", 6379)
if not ok then
    ngx.log(ngx.ERR, "failed to connect redis: ", err)
end

local buff = nil
local readSize = 4
--main loop
while true do
    -- 获取数据
    if not sock then
        ngx.log(ngx.ERR, "sock not found")
    end
    -- sock:settimeout(5000)  -- one second timeout
    local data, err, partial = sock:receive(readSize)
    -- 如果连接损坏 退出
    if err and err ~= "timeout" then
        -- ngx.log(ngx.ERR, "partial", partial)
        ngx.log(ngx.ERR, "sock error: ",  err)
        ngx.log(ngx.ERR, "partial:", partial, #partial)
        -- if partial then
        --     buff = buff .. partial
        -- end
        -- ngx.log(ngx.ERR, err and true)

        break
    end
    if data then
        if buff then
            buff = buff .. data
        else 
            buff = data
        end
        -- ngx.log(ngx.ERR, "#buff " .. #buff)
        -- ngx.log(ngx.ERR, "read len" .. xbit.readBuff(_uint32_t, buff, 0))
        local totalLen = xbit.readBuff(_uint32_t, buff, 0)
        -- readSize = totalLen
        -- ngx.log(ngx.ERR, totalLen)
        if totalLen > 5000 then
            ngx.log(ngx.ERR, totalLen)
            break
        end
        while #buff > 4 and #buff >= totalLen + 4 do
            local len = xbit.readBuff(_uint32_t, buff, 0)
            -- ngx.log(ngx.ERR, xbit.tohex(string.sub(buff, 1, 4)))
            -- ngx.log(ngx.ERR, len)
            if len >= 2 then
                local body = string.sub(buff, 5, 4 + len)
                if not fid then
                    fid = xbit.readBuff(_uint32_t, body, 1)
                end

                local msgId = xbit.readBuff(_uint8_t, body, 0)
                
                if not protocal._protocal[msgId] then
                    
                end
                -- inmediate response

                response(msgId, fid, body, red_p)

                gatherData(msgId, fid, body, red_set)
                local res, err = red_p:publish(_ws, body)
                if not res then
                    ngx.log(ngx.ERR, "failed to publish redis: ", err)
                    return ngx.exit(444)
                end
                
            end
            buff = string.sub(buff, 5 + len)
        end
    end 
end
ngx.log(ngx.ERR, "socket closing3")
-- sock:send_close()

ngx.thread.kill(co)